// This file is part of meshoptimizer library and is distributed under the terms of MIT License.
// Copyright (C) 2016-2022, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
var MeshoptDecoder = (function () {
  "use strict";

  var wasmpack = new Uint8Array([
    32, 0, 65, 2, 1, 106, 34, 33, 3, 128, 11, 4, 13, 64, 6, 253, 10, 7, 15, 116, 127, 5, 8, 12, 40, 16, 19, 54, 20, 9, 27, 255,
    113, 17, 42, 67, 24, 23, 146, 148, 18, 14, 22, 45, 70, 69, 56, 114, 101, 21, 25, 63, 75, 136, 108, 28, 118, 29, 73, 115,
  ]);

  if (typeof WebAssembly !== "object") {
    return {
      supported: false,
    };
  }
  var instance;

  var ready = WebAssembly.instantiate("assest/wasm/meshopt.wasm", {}).then(function (result: any) {
    instance = result.instance;
    instance.exports.__wasm_call_ctors();
  });

  //   let a = document.createElement("a");
  //   a.download = "meshopt.wasm";
  //   a.href = URL.createObjectURL(new Blob([unpack(wasm_base)], { type: "application/wasm" }));

  //   document.body.appendChild(a);
  //   a.click();
  //   var ready;
  //   fetch("../assest/wasm/wat.wasm")
  //     .then((res) => res.arrayBuffer())
  //     .then((bf) => {
  //       ready = WebAssembly.instantiate(bf, {}).then(function (result: any) {
  //         instance = result.instance;
  //         instance.exports.__wasm_call_ctors();
  //         console.log(1);
  //       });
  //     });

  function unpack(data) {
    var result = new Uint8Array(data.length);
    for (var i = 0; i < data.length; ++i) {
      var ch = data.charCodeAt(i);
      result[i] = ch > 96 ? ch - 97 : ch > 64 ? ch - 39 : ch + 4;
    }
    var write = 0;
    for (var i = 0; i < data.length; ++i) {
      result[write++] = result[i] < 60 ? wasmpack[result[i]] : (result[i] - 60) * 64 + result[++i];
    }
    return result.buffer.slice(0, write);
  }

  function decode(fun, target, count, size, source, filter?) {
    var sbrk = instance.exports.sbrk;
    var count4 = (count + 3) & ~3;
    var tp = sbrk(count4 * size);
    var sp = sbrk(source.length);
    var heap = new Uint8Array(instance.exports.memory.buffer);
    heap.set(source, sp);
    var res = fun(tp, count, size, sp, source.length);
    if (res == 0 && filter) {
      filter(tp, count4, size);
    }
    target.set(heap.subarray(tp, tp + count * size));
    sbrk(tp - sbrk(0));
    if (res != 0) {
      throw new Error("Malformed buffer data: " + res);
    }
  }

  var filters = {
    NONE: "",
    OCTAHEDRAL: "meshopt_decodeFilterOct",
    QUATERNION: "meshopt_decodeFilterQuat",
    EXPONENTIAL: "meshopt_decodeFilterExp",
  };

  var decoders = {
    ATTRIBUTES: "meshopt_decodeVertexBuffer",
    TRIANGLES: "meshopt_decodeIndexBuffer",
    INDICES: "meshopt_decodeIndexSequence",
  };

  var workers: { object: Worker; pending: number; requests: object }[] = [];
  var requestId = 0;

  function createWorker(url) {
    var worker = {
      object: new Worker(url),
      pending: 0,
      requests: {},
    };

    worker.object.onmessage = function (event) {
      var data = event.data;

      worker.pending -= data.count;
      worker.requests[data.id][data.action](data.value);

      delete worker.requests[data.id];
    };

    return worker;
  }

  //   function initWorkers(count) {
  //     var source =
  //       "var instance; var ready = WebAssembly.instantiate(new Uint8Array([" +
  //       new Uint8Array(unpack(wasm)) +
  //       "]), {})" +
  //       ".then(function(result) { instance = result.instance; instance.exports.__wasm_call_ctors(); });" +
  //       "self.onmessage = workerProcess;" +
  //       decode.toString() +
  //       workerProcess.toString();

  //     var blob = new Blob([source], { type: "text/javascript" });
  //     var url = URL.createObjectURL(blob);

  //     for (var i = 0; i < count; ++i) {
  //       workers[i] = createWorker(url);
  //     }

  //     URL.revokeObjectURL(url);
  //   }

  function decodeWorker(count, size, source, mode, filter) {
    var worker = workers[0];

    for (var i = 1; i < workers.length; ++i) {
      if (workers[i].pending < worker.pending) {
        worker = workers[i];
      }
    }

    return new Promise(function (resolve, reject) {
      var data = new Uint8Array(source);
      var id = requestId++;

      worker.pending += count;
      worker.requests[id] = { resolve: resolve, reject: reject };
      worker.object.postMessage({ id: id, count: count, size: size, source: data, mode: mode, filter: filter }, [data.buffer]);
    });
  }

  function workerProcess(event) {
    ready.then(function () {
      var data = event.data;
      try {
        var target = new Uint8Array(data.count * data.size);
        decode(instance.exports[data.mode], target, data.count, data.size, data.source, instance.exports[data.filter]);
        (self as any).postMessage({ id: data.id, count: data.count, action: "resolve", value: target }, [target.buffer]);
      } catch (error) {
        self.postMessage({ id: data.id, count: data.count, action: "reject", value: error });
      }
    });
  }

  return {
    ready: ready,
    supported: true,
    decodeVertexBuffer: function (target, count, size, source, filter) {
      decode(instance.exports.meshopt_decodeVertexBuffer, target, count, size, source, instance.exports[filters[filter]]);
    },
    decodeIndexBuffer: function (target, count, size, source) {
      decode(instance.exports.meshopt_decodeIndexBuffer, target, count, size, source);
    },
    decodeIndexSequence: function (target, count, size, source) {
      decode(instance.exports.meshopt_decodeIndexSequence, target, count, size, source);
    },
    decodeGltfBuffer: function (target, count, size, source, mode, filter) {
      decode(instance.exports[decoders[mode]], target, count, size, source, instance.exports[filters[filter]]);
    },
    decodeGltfBufferAsync: function (count, size, source, mode, filter) {
      if (workers.length > 0) {
        return decodeWorker(count, size, source, decoders[mode], filters[filter]);
      }

      return ready.then(function () {
        var target = new Uint8Array(count * size);
        decode(instance.exports[decoders[mode]], target, count, size, source, instance.exports[filters[filter]]);
        return target;
      });
    },
  };
})();

export { MeshoptDecoder };
